/*
 * AboutDialog.java
 *
 * Created on Aug 28, 2011, 7:24:14 PM
 */
package com.mrogrape.gui;

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.net.URI;
import java.net.URL;

import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.LayoutStyle;
import javax.swing.WindowConstants;

import com.mrogrape.util.ResourceManager;
import com.mrogrape.util.StringUtils;


/**
 * 关于对话框的实现，用以向用户展示程序的一些信息的一个小对话框。
 * 
 * <p>
 * 此对话框默认可以展示以下的程序信息。
 * <ul>
 * <li>图片：一个与你的应用程序相关的图片，显示在对话框的上部，推荐大小450*100像素。
 * <li>产品名：你的应用程序的名称。
 * <li>版本：应用程序的更新版本。
 * <li>版权：应用程序的版权归属。
 * <li>公司：你的公司或团队名称，抑或是父项目名。
 * <li>支持：一个链接，用以链接到你的应用程序的Web支持页面。这个组件已经添加了动作，
 * 在点击时会自定打开指定的URI。
 * <li>描述：对你的应用程序的详细描述。这是一个文本域并提供HTML以及在线页面的有限支持。
 * </ul>
 * 这些信息只有在你设置之后才显示。另外应用程序名，版本，版权，公司，支持这几个项完全
 * 可以设置为任意的你需要设置的内容，而不必拘泥与上面规定的那几项。图片会默认显示，如果
 * 需要禁用，可以设置picturePaneVisible的属性为false
 * 
 * <p>
 * 以上设置的项目会被展示在基本信息面板，如果你需要添加自己的面板，你可以使用
 * {@link #addCustomComponent(String, Component)}来实现。
 * 
 * <p>
 * 关于对话框默认是有模式的。
 * 
 * <p>
 * 除特殊说明，此类的方法都是线程安全的。
 *
 * @author Ice Spring
 * @version 0.9.20110831
 * @see #setPicture(Image)
 * @see #setProductName(String)
 * @see #setVersion(String)
 * @see #setCopyright(String)
 * @see #setCompanyName(String)
 * @see #setSupport(String, URI)
 * @see #setDescription(String)
 */
public class AboutDialog extends JDialog {

	/** SerialVersionUID */
	private static final long serialVersionUID = 740930812573100168L;

	private static final ResourceManager resources = new ResourceManager(AboutDialog.class);

	/**
	 * 创建默认的关于对话框。
	 */
	public AboutDialog(){
		this((Frame)null);
	}

	/**
	 * 创建关于对话框，并制定所有者。
	 * 
	 * @param owner 该对话框的所有者。
	 */
	public AboutDialog(Frame owner){
		this(owner,"About");
	}

	/**
	 * 创建关于对话框，并指定标题。
	 * 
	 * @param title 对话框标题。
	 */
	public AboutDialog(String title){
		this(null,title);
	}

	/**
	 * 创建关于对话框并指定对话框所有者和标题。
	 * 
	 * @param owner 对话框所有者。
	 * @param title 对话框标题。
	 */
	public AboutDialog(Frame owner,String title) {
		super(owner, title, true);

		this.initComponents();
	}

	/**
	 * 创建关于对话框并指定其所有者，标题，图像，产品名，版本，版权，公司，支持，和
	 * 描述信息。
	 * 
	 * @param owner 对话框所有者。
	 * @param title 对话框标题。
	 * @param image 产品图片
	 * @param productName 产品名
	 * @param version 版本
	 * @param copyright 版权
	 * @param company 公司名
	 * @param support 支持链接
	 * @param description 产品描述信息
	 */
	public AboutDialog(Frame owner, String title, Image image,
			String productName, String version, String copyright,
			String company, URI support, String description) {

		super(owner, title, true);
		this.initComponents();

		this.setPicture(image);
		this.setProductName(productName);
		this.setVersion(version);
		this.setCopyright(copyright);
		this.setCompanyName(company);
		this.setSupport(support.toString(), support);
		this.setDescription(description);
	}

	/**
	 * 设置程序图片。
	 * 
	 * @param image 程序图片。
	 */
	public void setPicture(final Image image){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.picturePane.setImage(image);
			}

		});
	}

	/**
	 * 获取设置的程序图片。
	 * 
	 * @return 设置的程序图片，如果从未设置过，将返回默认图片的对象。
	 */
	public Image getPicture(){

		return this.picturePane.getImage();
	}

	/**
	 * 设置是否显示应用程序图片。
	 * 
	 * @param visible 表示是否显示应用程序的boolean值：
	 * 					true  : 显示。
	 * 					fakse : 不显示。
	 */
	public void setPicturePaneVisible(final boolean visible){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.picturePane.setVisible(visible);
			}

		});
	}

	/**
	 * 查看应用程序图片的显示策略。
	 * 
	 * @return 表示是否显示应用程序的boolean值：
	 * 			true  : 显示。
	 * 			fakse : 不显示。
	 */
	public boolean getPicturePaneVisible(){

		return this.picturePane.isVisible();
	}

	/**
	 * 设置产品名信息。
	 * 
	 * @param name 产品名信息。
	 */
	public void setProductName(final String name){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {
				AboutDialog.this.produceNameView.setVisible(StringUtils.isNotEmpty(name));
				AboutDialog.this.produceNameView.setText(name);
			}

		});
	}

	/**
	 * 获取设置的应用程序名信息。
	 * 
	 * @return 设置的应用程序名信息。
	 */
	public String getProductName(){

		return this.produceNameView.getText();
	}

	/**
	 * 设置应用程序版本号。
	 * 
	 * @param version 应用程序版本号。
	 */
	public void setVersion(final String version){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.versionView.setVisible(StringUtils.isNotEmpty(version));
				AboutDialog.this.versionView.setText(version);

			}

		});
	}

	/**
	 * 获取设置的应用程序版本号信息。
	 * 
	 * @return 应用程序版本号信息。
	 */
	public String getVersion(){

		return this.versionView.getText();
	}

	/**
	 * 设置应用程序版权信息。
	 * 
	 * @param copyright 应用程序版权信息。
	 */
	public void setCopyright(final String copyright){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.copyrightView.setVisible(StringUtils.isNotEmpty(copyright));
				AboutDialog.this.copyrightView.setText(copyright);
			}

		});
	}

	/**
	 * 获取设置的应用程序版权。
	 * 
	 * @return 应用程序版权。
	 */
	public String getCopyright(){

		return this.copyrightView.getText();
	}

	/**
	 * 设置应用程序开发商信息。
	 * 
	 * @param company 应用程序开发商信息。
	 */
	public void setCompanyName(final String company){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.companyNameView.setVisible(StringUtils.isNotEmpty(company));
				AboutDialog.this.companyNameView.setText(company);
			}

		});
	}

	/**
	 * 获取设置的应用程序开发商信息。
	 * 
	 * @return 应用程序开发商信息。
	 */
	public String getCompanyName(){

		return this.companyNameView.getText();
	}

	/**
	 * 设置应用程序支持信息。
	 * 
	 * <p>
	 * 这个信息的展示组件已经添加了动作，在点击时会自定打开指定的URI。
	 * 
	 * @param text 需要显示的文本。
	 * @param uri 点击该信息时需要打开的链接的URI。
	 */
	public void setSupport(final String text,final URI uri){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.supportView.setVisible(StringUtils.isNotEmpty(text) || uri!=null);
				AboutDialog.this.supportView.setText(text);
				AboutDialog.this.supportView.setURI(uri);
			}

		});
	}

	/**
	 * 设置应用程序支持信息文本。
	 * 
	 * @param text 应用程序支持信息显示的文本。
	 */
	public void setSupportText(String text){

		this.setSupport(text, this.supportView.getURI());
	}

	/**
	 * 获取设置的应用程序支持信息文本。
	 * 
	 * @return 应用程序支持信息文本。
	 */
	public String getSupportText(){

		return this.supportView.getText();
	}

	/**
	 * 获取应用程序支持信息指向的URI。
	 * 
	 * @return 应用程序支持信息指向的URI。
	 */
	public URI getSupportURI(){

		return this.supportView.getURI();
	}

	/**
	 * 设置应用程序支持信息指向的URI。
	 * 
	 * @param uri 应用程序支持信息指向的URI。
	 */
	public void setSupportURI(URI uri){

		this.setSupport(this.supportView.getText(), uri);
	}

	/**
	 * 设置应用程序描述信息，可以使用HTML格式文本。
	 * 
	 * @param description 应用程序描述信息。
	 */
	public void setDescription(final String description){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {
				AboutDialog.this.descriptionScrollPane.setVisible(StringUtils.isNotEmpty(description));
			}

		});

		this.descriptionView.setText(description);
	}

	/**
	 * 获取应用程序描述文本。
	 * 
	 * @return 应用程序描述文本。
	 */
	public String getDescription(){

		return this.descriptionView.getText();
	}

	/**
	 * 通过一个URL来设置描述信息。
	 * 
	 * <p>
	 * URL指向的可以是一个网页或者本地的文本文档，对HTML仅提供很有限的支持。
	 * 
	 * <p>
	 * <b>警告：</b>此方法不是线程安全的。
	 * 
	 * @param url 需要显示的资源的URL，不可以为null。
	 * @throws IOException 如果读取过程中发生I/O异常.
	 */
	public void setDescriptionPage(URL url) throws IOException {

		this.setDescription("Could not open URL : " + url);
		AboutDialog.this.descriptionView.setPage(url);

	}

	/**
	 * 设置程序描述信息的标题。
	 * 
	 * @param title 程序描述信息的标题。
	 */
	public void setDescriptionTitile(final String title){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.descriptionView.setBorder(BorderFactory
						.createTitledBorder(title));

			}

		});
	}

	/**
	 * 添加自定义组件。
	 * 
	 * @param title 自定义组件的标题。
	 * @param c 自定义组件。
	 */
	public void addCustomComponent(final String title, final Component c){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.infoPane.add(title, c);
			}

		});
	}

	/**
	 * 移除自定义组件。
	 * 
	 * @param c 需要移除的自定义组件。
	 */
	public void removeCustomComponent(final Component c){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.infoPane.remove(c);
			}

		});

	}

	/**
	 * 设置自定义组件的标题。
	 * 
	 * @param c 需要设置的自定义组件。
	 * @param newTitile 新的标题。
	 */
	public void setCustomComponentTitile(final Component c, final String newTitile){

		EventQueue.invokeLater(new Runnable(){

			@Override
			public void run() {

				AboutDialog.this.infoPane.setTitleAt(AboutDialog.this.infoPane
						.indexOfComponent(c), newTitile);
			}

		});

	}

	/**
	 * 获取指定索引位置的组件。
	 * 
	 * <p>
	 * 注意，O号组件为基本信息面板，不可获取，当index为0时会返回null.
	 * 
	 * @param index 需要获取组件的索引。
	 * @return 指定索引位置的组件。
	 */
	public Component getCustomComponentAt(int index){

		return index == 0?null:this.infoPane.getComponentAt(index);
	}

	/** This method is called from within the constructor to
	 * initialize the form.
	 */
	private void initComponents() {

		this.picturePane = new PicturePane();
		this.buttonPanel = new JPanel();
		this.okBt = new JButton();
		this.infoPane = new JTabbedPane();
		this.baseInfoPanel = new JPanel();
		this.produceNameView = new JLabel();
		this.versionView = new JLabel();
		this.copyrightView = new JLabel();
		this.companyNameView = new JLabel();
		this.supportView = new LinkLabel();
		this.descriptionScrollPane = new JScrollPane();
		this.descriptionView = new JEditorPane("text/html","");

		this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		this.setTitle(AboutDialog.resources.getString("title"));
		this.setAlwaysOnTop(true);
		this.setModal(true);
		this.setName(AboutDialog.resources.getString("dialog_name"));
		//this.setResizable(false);

		this.picturePane.setPreferredSize(new java.awt.Dimension(420, 200));

		GroupLayout picturePaneLayout = new GroupLayout(this.picturePane);
		this.picturePane.setLayout(picturePaneLayout);
		this.picturePane.setResizeMode(PicturePane.FILL);
		this.picturePane.setImage(AboutDialog.resources.createImageIcon(
				AboutDialog.resources.getString("image.file"),
				AboutDialog.resources.getString("image.desc")).getImage());
		picturePaneLayout.setHorizontalGroup(
				picturePaneLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addGap(0, 450, Short.MAX_VALUE)
		);
		picturePaneLayout.setVerticalGroup(
				picturePaneLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addGap(0, 100, Short.MAX_VALUE)
		);

		this.okBt.setText(AboutDialog.resources.getString("ok_button.text"));
		this.okBt.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent evt) {
				AboutDialog.this.okBtActionPerformed(evt);
			}
		});

		GroupLayout buttonPanelLayout = new GroupLayout(this.buttonPanel);
		this.buttonPanel.setLayout(buttonPanelLayout);
		buttonPanelLayout.setHorizontalGroup(
				buttonPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addGroup(GroupLayout.Alignment.TRAILING, buttonPanelLayout.createSequentialGroup()
						.addContainerGap(395, Short.MAX_VALUE)
						.addComponent(this.okBt)
						.addContainerGap())
		);
		buttonPanelLayout.setVerticalGroup(
				buttonPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addComponent(this.okBt)
		);

		this.produceNameView.setVisible(false);

		this.versionView.setVisible(false);

		this.copyrightView.setVisible(false);

		this.companyNameView.setVisible(false);

		this.supportView.setVisible(false);

		this.descriptionView.setBorder(BorderFactory.createTitledBorder(AboutDialog.resources.getString("description_view.title")));
		this.descriptionView.setEditable(false);
		this.descriptionScrollPane.setViewportView(this.descriptionView);
		this.descriptionScrollPane.setVisible(false);

		GroupLayout baseInfoPanelLayout = new GroupLayout(this.baseInfoPanel);
		this.baseInfoPanel.setLayout(baseInfoPanelLayout);
		baseInfoPanelLayout.setHorizontalGroup(
				baseInfoPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addGroup(baseInfoPanelLayout.createSequentialGroup()
						.addContainerGap()
						.addGroup(baseInfoPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
								.addComponent(this.descriptionScrollPane, GroupLayout.DEFAULT_SIZE, 425, Short.MAX_VALUE)
								.addComponent(this.produceNameView)
								.addComponent(this.versionView)
								.addComponent(this.copyrightView)
								.addComponent(this.companyNameView)
								.addComponent(this.supportView))
								.addContainerGap())
		);
		baseInfoPanelLayout.setVerticalGroup(
				baseInfoPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addGroup(baseInfoPanelLayout.createSequentialGroup()
						.addContainerGap()
						.addComponent(this.produceNameView)
						.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.versionView)
						.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.copyrightView)
						.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.companyNameView)
						.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.supportView)
						.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.descriptionScrollPane, GroupLayout.DEFAULT_SIZE, 157, Short.MAX_VALUE)
						.addContainerGap())
		);

		this.infoPane.addTab(AboutDialog.resources.getString("base_info_pane.title"), this.baseInfoPanel);

		GroupLayout layout = new GroupLayout(this.getContentPane());
		this.getContentPane().setLayout(layout);
		layout.setHorizontalGroup(
				layout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addComponent(this.buttonPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
				.addComponent(this.picturePane, GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE)
				.addComponent(this.infoPane, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 450, Short.MAX_VALUE)
		);
		layout.setVerticalGroup(
				layout.createParallelGroup(GroupLayout.Alignment.LEADING)
				.addGroup(layout.createSequentialGroup()
						.addComponent(this.picturePane, GroupLayout.PREFERRED_SIZE, 100, GroupLayout.PREFERRED_SIZE)
						.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.infoPane, GroupLayout.DEFAULT_SIZE, 311, Short.MAX_VALUE)
						.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
						.addComponent(this.buttonPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
						.addContainerGap())
		);

		this.pack();
	}

	private void okBtActionPerformed(ActionEvent evt) {
		this.setVisible(false);
	}

	//
	// Swing Components
	//

	private JPanel baseInfoPanel;
	private JPanel buttonPanel;
	private JLabel companyNameView;
	private JLabel copyrightView;
	private JScrollPane descriptionScrollPane;
	private JEditorPane descriptionView;
	private JTabbedPane infoPane;
	private JButton okBt;
	private PicturePane picturePane;
	private JLabel produceNameView;
	private LinkLabel supportView;
	private JLabel versionView;
}
